use crate::MathUtils::MathUtils;
use crate::Point::Point;
use crate::Vector2::Vector2;
use crate::Vector3::Vector3;

pub struct GeometryTool;

impl GeometryTool {
    pub fn vector3toVector2(v: &Vector3) -> Vector2 {
        Vector2::new(v.x, v.z)
    }

    pub fn relativeCoordinates(v: &Vector3, subjectV: &Vector3) -> Vector3 {
        Vector3::new(v.x - subjectV.x, v.y - subjectV.y, v.z - subjectV.z)
    }

    pub fn vector3toVector2Revit(v: &Vector3) -> Vector2 {
        Vector2::new(v.x, v.y)
    }

    pub fn interpolate(vec1: &Vector2, vec2: &Vector2, num: f64) -> Vector2 {
        Vector2::new(
            vec1.x + (vec2.x - vec1.x) * num,
            vec1.y + (vec2.y - vec1.y) * num,
        )
    }

    pub fn normal(vertices: &[Vector3]) -> Option<Vector3> {
        if vertices.len() < 3 {
            return None;
        }
        let v0 = GeometryTool::subtract(&vertices[1], &vertices[0]);
        let v1 = GeometryTool::subtract(&vertices[2], &vertices[0]);
        let result = GeometryTool::cross(&v0, &v1);
        Some(result)
    }

    pub fn subtract(v: &Vector3, v1: &Vector3) -> Vector3 {
        Vector3::new(v.x - v1.x, v.y - v1.y, v.z - v1.z)
    }

    pub fn cross(v0: &Vector3, v1: &Vector3) -> Vector3 {
        let var3 = v0.y * v1.z - v0.z * v1.y;
        let var5 = v1.x * v0.z - v1.z * v0.x;
        let mut result = Vector3::new(0.0, 0.0, 0.0);
        result.z = v0.x * v1.y - v0.y * v1.x;
        result.x = var3;
        result.y = var5;
        result
    }

    pub fn dot(v0: &Vector3, var1: &Vector3) -> f64 {
        v0.x * var1.x + v0.y * var1.y + v0.z * var1.z
    }

    pub fn normalized(v: &Vector3) -> Vector3 {
        let length = 1.0 / (v.x * v.x + v.y * v.y + v.z * v.z).sqrt();
        Vector3::new(v.x * length, v.y * length, v.z * length)
    }

    pub fn is_left(P0: &Vector2, P1: &Vector2, P2: &Vector2) -> bool {
        (P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y) > 0.0
    }

    pub fn rotation_transform(from: &Vector2, alpha: f64) -> Vector2 {
        let new_x = from.x * alpha.cos() - from.y * alpha.sin();
        let new_y = from.x * alpha.sin() + from.y * alpha.cos();
        Vector2::new(new_x, new_y)
    }

    pub fn arc(p: &Vector2, p1: &Vector2) -> f64 {
        (p1.y - p.y).atan2(p1.x - p.x)
    }

    pub fn is_left_sign_v(P0: &Vector2, P1: &Vector2, P2: &Vector2) -> i32 {
        MathUtils::sign((P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y))
    }
    pub fn is_left_sign(P0: &Point, P1: &Point, P2: &Point) -> i32 {
        MathUtils::sign((P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y))
    }

    pub fn same_side_v(p0: &Vector2, p1: &Vector2, p2: &Vector2, p3: &Vector2) -> bool {
        let b2 = GeometryTool::is_left_sign_v(p0, p1, p2);
        let b3 = GeometryTool::is_left_sign_v(p0, p1, p3);
        b2 * b3 > 0
    }

    pub fn same_side(p0: &Point, p1: &Point, p2: &Point, p3: &Point) -> bool {
        let b2 = GeometryTool::is_left_sign(p0, p1, p2);
        let b3 = GeometryTool::is_left_sign(p0, p1, p3);
        b2 * b3 > 0
    }

    pub fn same_side_sim_v(p0: &Vector2, p1: &Vector2, p2: &Vector2, p3: &Vector2) -> bool {
        let b2 = GeometryTool::is_left_sign_v(p0, p1, p2);
        let b3 = GeometryTool::is_left_sign_v(p0, p1, p3);
        b2 * b3 >= 0
    }

    pub fn same_side_sim(p0: &Point, p1: &Point, p2: &Point, p3: &Point) -> bool {
        let b2 = GeometryTool::is_left_sign(p0, p1, p2);
        let b3 = GeometryTool::is_left_sign(p0, p1, p3);
        b2 * b3 >= 0
    }
}
